package edu.ntnu.superrss.models;

import com.sun.syndication.feed.impl.ObjectBean;
import com.sun.syndication.feed.impl.CopyFromHelper;
import com.sun.syndication.feed.WireFeed;
import com.sun.syndication.feed.module.*;
import com.sun.syndication.feed.synd.Converter;
import com.sun.syndication.feed.synd.SyndContent;
import com.sun.syndication.feed.synd.SyndEntry;
import com.sun.syndication.feed.synd.SyndImage;
import com.sun.syndication.feed.synd.impl.Converters;

import java.util.*;
import java.io.Serializable;
import javax.persistence.*;

/**
 * This is a close to a copy of SyndFeedImpl in ROME, with hibernate annotations
 * We stripped some of the functionality that we feal is not important for this project
 * @author Sigurd Fosseng <sigurd at fosseng.net>
 */
@Entity
@Table(name = "SYND_FEED")
public class SyndFeedImpl implements Serializable, SyndFeedExt {

	private Long id;
	private ObjectBean objBean;
	private String title;
	private String uri;
	private String link;
	private WireFeed wireFeed;
	private boolean preservedWireFeed;
	private String author;
	private String copyright;
	private String description;
	private String language;
	private String feedType;
	private String encoding;
	private List<String> authors;
	private List links;
	private Date publishedDate;
	private SyndImage image;
	private List<SyndEntry> entries = new ArrayList<SyndEntry>();
        private List<Category> categories = new ArrayList<Category>();
	private List modules;

	private static final Converters CONVERTERS = new Converters();
	private static final Set IGNORE_PROPERTIES = new HashSet();
	/**
	 * Unmodifiable Set containing the convenience properties of this class.
	 * <p>
	 * Convenience properties are mapped to Modules, for cloning the convenience properties
	 * can be ignored as the will be copied as part of the module cloning.
	 */
	public static final Set CONVENIENCE_PROPERTIES = Collections.unmodifiableSet(IGNORE_PROPERTIES);

	static {
		//IGNORE_PROPERTIES.add("publishedDate");
		IGNORE_PROPERTIES.add("author");
		IGNORE_PROPERTIES.add("copyright");
		IGNORE_PROPERTIES.add("categories");
		IGNORE_PROPERTIES.add("language");
	}
	
	private Date lastUpdate;
	private String url;
	
	

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

      protected SyndFeedImpl(Class beanClass, Set convenienceProperties) {
		objBean = new ObjectBean(beanClass, this, convenienceProperties);
	}

	public SyndFeedImpl() {
		this(null);
	}

	public SyndFeedImpl(WireFeed feed) {
		this(feed, false);
	}

	public SyndFeedImpl(WireFeed feed, boolean preservedWireFeed) {
		this(SyndFeedExt.class, IGNORE_PROPERTIES);
		if (preservedWireFeed) {
			this.wireFeed = feed;
			this.preservedWireFeed = preservedWireFeed;
		}
		if (feed != null) {
			feedType = feed.getFeedType();
			Converter converter = CONVERTERS.getConverter(feedType);
			if (converter == null) {
				throw new IllegalArgumentException("invalid feedtype:" + feedType);
			}
			converter.copyInto(feed, this);
		}
	}

	@Transient
	public List getSupportedFeedTypes() {
		return CONVERTERS.getSupportedFeedTypes();
	}

	public WireFeed createWireFeed() {
		return createWireFeed(feedType);
	}

	public WireFeed createWireFeed(String feedType) {
		if (feedType == null) {
			throw new IllegalArgumentException("feedType cannot be null");
		}
		Converter converter = CONVERTERS.getConverter(feedType);
		if (converter == null) {
			throw new IllegalArgumentException("Invalid feedType: " + feedType);
		}
		return converter.createRealFeed(this);
	}

	public WireFeed originalWireFeed() {
		return wireFeed;
	}

	public boolean isPreservingWireFeed() {
		return preservedWireFeed;
	}

	public void setPreservingWireFeed(boolean preservedWireFeed) {
		this.preservedWireFeed = preservedWireFeed;
	}

	@Column(name = "FEED_TYPE")
	public String getFeedType() {
		return feedType;
	}

	public void setFeedType(String feedType) {
		this.feedType = feedType;
	}

	@Column(name = "ENCODING")
	public String getEncoding() {
		return encoding;
	}

	public void setEncoding(String encoding) {
		this.encoding = encoding;
	}

	@Column(name = "URI")
	public String getUri() {
		return uri;
	}

	public void setUri(String uri) {
		this.uri = uri;
	}

	@Column(name = "LINK")
	public String getLink() {
		return link;
	}

	public void setLink(String link) {
		this.link = link;
	}

	@Transient
	public List getLinks() {
		return (links == null) ? (links = new ArrayList()) : links;
	}

	public void setLinks(List links) {
		this.links = links;
	}

	@Column(name = "DESCRIPTION")
	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	@Transient
	//SyndContent for a simple description string is stupid. Fake it
	public SyndContent getDescriptionEx() {
		SyndContent s = new SyndContentImpl();
		s.setValue(this.description);
		return s;
	}

	public void setDescriptionEx(SyndContent description) {
		this.description = description.getValue();
	}

        @Temporal(javax.persistence.TemporalType.TIMESTAMP)
	public Date getPublishedDate() {
		return this.publishedDate;
	}

	public void setPublishedDate(Date publishedDate) {
		this.publishedDate = publishedDate;
	}

	@Transient
	public List getAuthors() {
		return authors;
	}

	public void setAuthors(List authors) {
		this.authors = authors;
	}

	public String getAuthor() {
		//Jeg kunne kanskje gjort DCModule hibernate-vennlig. men jeg tror String er nok for vårt bruk.
		return author;
	}

	public void setAuthor(String author) {
		this.author = author;
	}

	@Transient
	public List getContributors() {
		throw new UnsupportedOperationException("Not supported yet.");
	}

	public void setContributors(List contributors) {
		throw new UnsupportedOperationException("Not supported yet.");
	}

	public String getCopyright() {
		return this.copyright;
	}

	public void setCopyright(String copyright) {
		this.copyright = copyright;
	}

	@OneToOne(targetEntity=SyndImageImpl.class, cascade=CascadeType.ALL)
	public SyndImage getImage() {
		return image;
	}

	public void setImage(SyndImage image) {
		this.image = image;
	}

        /**
         * Not in use
         **/
//        @Transient
	@ManyToMany(cascade=CascadeType.ALL, targetEntity=Category.class)
	public List getCategories() {
//            return this.categories;
            return this.categories;
//		throw new UnsupportedOperationException("Not supported yet.");
	}
//
	public void setCategories(List categories) {
            this.categories = categories;
//		throw new UnsupportedOperationException("Not supported yet.");
	}

	@OneToMany(targetEntity=SyndEntryImpl.class,cascade=CascadeType.ALL, mappedBy="source")
	public List getEntries() {
		return entries;
	}

	public void setEntries(List entries) {
		this.entries  = entries;
		for (Iterator it = entries.iterator(); it.hasNext();) {
			SyndEntry entry = (SyndEntry) it.next();
			entry.setSource(this);
		}
	}

	@Column(name = "LANG")
	public String getLanguage() {
		return language;
	}

	public void setLanguage(String language) {
		this.language = language;
	}

	public Module getModule(String uri) {
		throw new UnsupportedOperationException("Not supported yet.");
	}

	@Transient
	public List getModules() {
		return  modules;
	}

	public void setModules(List modules) {
		this.modules = modules;
	}

	@Transient
	//I dont know if we need this. Ignore.
	public Object getForeignMarkup() {
		return new ArrayList(); //
	}

	public void setForeignMarkup(Object foreignMarkup) {
		// do nothing for now
	}

	@Transient
	public Class getInterface() {
		return SyndFeedExt.class;
	}

	

	@Override
	public Object clone() throws CloneNotSupportedException {
		return objBean.clone();
	}

	@Transient
	//SyndContent for Strings is stupid, lets fake it. Stupid interface. -S
	public SyndContent getTitleEx() {
		SyndContent s = new SyndContentImpl();
		s.setValue(title);
		return s;
	}

	public void setTitleEx(SyndContent title) {
		this.title =  title.getValue();
	}

	@Temporal(javax.persistence.TemporalType.TIMESTAMP)
	public Date getLastUpdate() {
		return lastUpdate;
	}

	public void setLastUpdate(Date lastUpdate) {
		this.lastUpdate = lastUpdate;
	}
	public String getUrl() {
		return url;
	}
	public void setUrl(String url) {
		this.url = url;
	}

	@Id
	@GeneratedValue
	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}
	public boolean equals(SyndFeedImpl feed){
		return feed.getId().equals(id);
	}
	private static final CopyFromHelper COPY_FROM_HELPER;

      static {
            Map basePropInterfaceMap = new HashMap();
            basePropInterfaceMap.put("feedType", String.class);
            basePropInterfaceMap.put("encoding", String.class);
            basePropInterfaceMap.put("uri", String.class);
            basePropInterfaceMap.put("title", String.class);
            basePropInterfaceMap.put("link", String.class);
            basePropInterfaceMap.put("description", String.class);
            basePropInterfaceMap.put("image", SyndImage.class);
            basePropInterfaceMap.put("entries", SyndEntry.class);
            basePropInterfaceMap.put("modules", Module.class);
            basePropInterfaceMap.put("publishedDate", Date.class);

            Map basePropClassImplMap = new HashMap();
            basePropClassImplMap.put(SyndEntry.class, SyndEntryImpl.class);
            basePropClassImplMap.put(SyndImage.class, SyndImageImpl.class);
            basePropClassImplMap.put(DCModule.class, DCModuleImpl.class);
            basePropClassImplMap.put(SyModule.class, SyModuleImpl.class);

            COPY_FROM_HELPER = new CopyFromHelper(SyndFeedExt.class, basePropInterfaceMap, basePropClassImplMap);
      }
      public void copyFrom(Object obj) {
            COPY_FROM_HELPER.copy(this, obj);
      }
}
